package puzzle.masyu2.solver;

import puzzle.common.data.HatanException;
import puzzle.common.point.Direction;
import puzzle.masyu2.data.MasyuBoard;

class DangoRule extends MasyuCellRule {

	@Override
	public boolean apply(MasyuBoard b, int x, int y) throws HatanException {
		boolean isChanged = false;
		switch (b.getDangoType(x, y)) {
		case KURO:
			isChanged |= applyKuroRule(b, x, y);
			break;
		case SHIRO:
			isChanged |= applyShiroRule(b, x, y);
			break;
		}
		return isChanged;
	}

	private boolean applyKuroRule(MasyuBoard b, int x, int y) throws HatanException {
		boolean isChanged = false;

		for (Direction d : Direction.CLOCKWISE4) {
			int x1 = x + d.v.dx;
			int y1 = y + d.v.dy;
			if (b.getForbiddenFlag(x, y, d) || b.getForbiddenFlag(x1, y1, d)
					|| b.getExistFlag(x1, y1, d.rrot()) || b.getExistFlag(x1, y1, d.lrot())) {
				isChanged |= b.setForbiddenFlag(x, y, d);
				isChanged |= setLinesForKuro(b, x, y, d.inv());
			}
			if (b.getExistFlag(x, y, d)) {
				b.setForbiddenFlag(x, y, d.inv());
			}
		}

		if (b.getForbiddenCountAround(x, y) > 2 || b.getLineCountAround(x, y) > 2) {
			throw new HatanException();
		}

		return isChanged;
	}

	private boolean setLinesForKuro(MasyuBoard b, int x, int y, Direction d) throws HatanException {
		boolean isChanged = false;
		isChanged |= b.setExistFlag(x, y, d);
		isChanged |= b.setExistFlag(x + d.v.dx, y + d.v.dy, d);
		return isChanged;
	}

	private boolean applyShiroRule(MasyuBoard b, int x, int y) throws HatanException {
		boolean isChanged = false;

		isChanged |= applyShiroRuleForDirection(b, x, y, Direction.N);
		isChanged |= applyShiroRuleForDirection(b, x, y, Direction.W);

		if (b.getForbiddenCountAround(x, y) > 2 || b.getLineCountAround(x, y) > 2) {
			throw new HatanException();
		}

		return isChanged;
	}

	private boolean applyShiroRuleForDirection(MasyuBoard b, int x, int y, Direction d)
			throws HatanException {
		boolean isChanged = false;
		Direction di = d.inv();
		if (b.getExistFlag(x, y, d) || b.getExistFlag(x, y, di)) {
			isChanged |= b.setExistFlag(x, y, d);
			isChanged |= b.setExistFlag(x, y, di);
			isChanged |= b.setForbiddenFlag(x, y, d.rrot());
			isChanged |= b.setForbiddenFlag(x, y, d.lrot());

			isChanged |= applyShiroHookRule(b, x, y, d);
			isChanged |= applyShiroHookRule(b, x, y, di);

		} else if (b.getForbiddenFlag(x, y, d) || b.getForbiddenFlag(x, y, di)) {
			isChanged |= b.setForbiddenFlag(x, y, d);
			isChanged |= b.setForbiddenFlag(x, y, di);
			isChanged |= b.setExistFlag(x, y, d.rrot());
			isChanged |= b.setExistFlag(x, y, d.lrot());

			isChanged |= applyShiroHookRule(b, x, y, d.rrot());
			isChanged |= applyShiroHookRule(b, x, y, d.lrot());
		}
		return isChanged;
	}

	private boolean applyShiroHookRule(MasyuBoard b, int x, int y, Direction d)
			throws HatanException {
		boolean isChanged = false;
		if (b.getExistFlag(x + d.v.dx, y + d.v.dy, d)) {
			Direction d1 = d.inv();
			isChanged |= b.setForbiddenFlag(x + d1.v.dx, y + d1.v.dy, d1);
		}
		return isChanged;
	}

}
